package language.advanced.reflect;

import lombok.RequiredArgsConstructor;
import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class reflect {

    class Parent {}

    @RequiredArgsConstructor //为private final类型且无值的字段生成构造器
    class Son extends Parent {
        public String fieldA = "Enum";
        private final String privateField = "bbb";
        public void say(){
            System.out.println("say");
        }

        public Son(String param){
            System.out.println("param Constructor");
        }
        private Son(Integer param, String pa){
            System.out.println("private param Constructor");
        }
    }

    @Test
    public void typeCheck(){
        Son son = new Son();

        //1、使用==或者equals方法判断类型是否相等，语义清晰
        System.out.println(son.getClass() == Son.class); //结果和equals方法相同

        //2、instanceof和isInstance()除了相等，还可以判断继承关系
        //instanceof 运算符是用来在运行时指出对象obj是否是特定类的一个实例
        //如果有一个对象和一个类。  obj是ClassB的实例
        System.out.println(son instanceof Parent);
        System.out.println(son instanceof Son);
        //如果有的是class对象和一个对象
        System.out.println(Parent.class.isInstance(son));

        //Class和Object都有自己的class对象。
        Class<Class> classObjectOfClassClass = java.lang.Class.class;
        Class<Object> objectClass = Object.class;
        System.out.println(objectClass instanceof Object);

    }

    @Test
    public void achieveClass() throws Exception {
        //1.
        Class<Son> clzB1 = Son.class;

        //2. obj.getClass()
        Class<? extends Son> aClass = new Son().getClass();

        //3. Class.forName
        Class clzB2 = Class.forName(Son.class.getName());// "language.advanced.reflect.ClassB"
    }

    @Test
    public void a() throws Exception {
        Object obj = new Son();

        //getDeclaredField是可以获取一个类的所有字段.
        //getField只能获取类的public 字段.
        //访问公有属性，返回aaa
        String publicFieldContent = (String) obj.getClass().getField("fieldA").get(new Son());

        //访问私有属性，返回bbb
        Field privateField = obj.getClass().getDeclaredField("privateField");
        privateField.setAccessible(true);
        String privateFieldContent = (String) privateField.get(obj);

        //调用方法
        Method method = Son.class.getMethod("say");
        method.invoke(obj);  // obj.method()

        //newInstance构造新对象
        //使用 newInstance 时必须要保证这类已经加载并且已经建立连接，就是已经被类记载器加载完毕，而 new 不需要
        //1、使用默认的无参构造方法，deprecated
        Son son1 = Son.class.newInstance();

        //2、通过 Constructor 对象创建类对象可以选择特定构造方法
        //调用 public Son(String param) 这个构造器，考虑
        Son son2 = Son.class.getConstructor(String.class).newInstance("param");

        //调用 private的 Son(Integer param) 这个构造器
        Constructor<Son> declaredConstructor = Son.class.getDeclaredConstructor(Integer.class , String.class);
        declaredConstructor.setAccessible(true);
        Son son = declaredConstructor.newInstance(1, "aa");
        System.out.println("generate " + son);

    }

    @Test
    public void other() throws Exception {
        //通过Class对象判断类之间的继承关系
        //isAssignableFrom判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同，或是否是其超类或超接口
        Parent parent = new Parent();
        if (parent.getClass().isAssignableFrom(Son.class)){
            System.out.println("true");
        }

        System.out.println(Parent.class.getClassLoader());
        Package aPackage = parent.getClass().getPackage();
        System.out.println(aPackage);
    }
}
